package cz.cuni.amis.pogamut.episodic.schemas;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Collection;
import org.apache.commons.collections15.multimap.MultiHashMap;

/**
 * When the slot represented by this node is filled with an object node,
 * it is linked with the node representing that item via the <code>
 * SlotContent</code> class. Each <code>SlotContent</code> contains
 * references to exactly one <code>SchemaSlot</code> and one <code>
 * SchemaObjectNode</code> it is linking together.
 * <p>
 * Together with <code>SchemaEpisodeNode</code>s they form a set that is
 * indexing different schema counters. They keep list of references
 * to counters they contribute to.
 *
 * @author Michal Cermak
 */
public class SlotContent implements Serializable {
    /**
     * Determines if a de-serialized file is compatible with this class.
     *
     * Maintainers must change this value if and only if the new version
     * of this class is not compatible with old versions. See Sun docs
     * for <a href=http://java.sun.com/products/jdk/1.1/docs/guide
     * /serialization/spec/version.doc.html> details. </a>
     *
     * Not necessary to include in first version of the class, but
     * included here as a reminder of its importance.
     */
    private static final long serialVersionUID = 1L;

    /**
     * ID of this node. Used as an ID of vertex representing
     * this chronobag when visualizing Schemas.
     */
    final int id;

    /**
     * Reference to the <code>SchemaSlot</code> object on one end of the
     * link between a slot and an object created by this slot content.
     */
    final SchemaSlot slot;

    /**
     * Reference to the <code>SchemaObjectNode</code> object on one end of the
     * link between a slot and an object created by this slot content.
     */
    final SchemaObjectNode object;

    /**
     * <code>counts</code> is a collection of all <code>SchemaCounter</code>s
     * this node contributes to. In order to quickly access any counter,
     * counters are indexed in a multihashmap. <strong>Index of a counter is calculated
     * as the sum of IDs of all nodes it refers to</strong>, but when accessing
     * a counter from the inside of the node, nodes id is not added to the sum.
     * So a single counters of nodes can always be accessed on zero index.
     */
    public TemporalSchema counts = new TemporalSchema();

    /**
     * Instantiate the class by providing unique ID and references to both
     * <code>SchemaSlot</code> and <code>SchemaObjectNode</code> that are
     * to be connected by this slot content.
     *
     * @param   _id Unique ID of this slot content.
     * @param   _slot   Reference to the <code>SchemaSlot</code> object on
     * one end of the link between a slot and an object created by
     * this new slot content.
     * @param   _object   Reference to the <code>SchemaObjectNode</code>
     * object on one end of the link between a slot and an object created
     * by this new slot content.
     */
    public SlotContent(int _id, SchemaSlot _slot, SchemaObjectNode _object) {
        id = _id;
        slot = _slot;
        object = _object;
    }

    /**
     * Getter method for the <code>id</code> variable.
     *
     * @return  Returns ID of this node. Used as an ID of vertex representing
     * this chronobag when visualizing Schemas.
     */
    public int getId() {
        return id;
    }

    /**
     * Getter method for the <code>slot</code> variable.
     *
     * @return  Returns the reference to the <code>SchemaSlot</code> object
     * on one end of the link between a slot and an object created by
     * this new slot content.
     */
    public SchemaSlot getSlot() {
        return slot;
    }

    /**
     * Getter method for the <code>object</code> variable.
     *
     * @return  Returns the reference to the <code>SchemaObjectNode</code>
     * object  on one end of the link between a slot and an object created
     * by this new slot content.
     */
    public SchemaObjectNode getObject() {
        return object;
    }

    /**
     * Each <code>SlotContent</code> keeps the list of references
     * to counters it is contributing to (it is contained in set of nodes
     * defining the counter). So when a new counter is created, that
     * is co-defined by this slot content, it need to be added to this list.
     * <p>
     * This method adds a speficied counter to this list under a specified
     * key.
     * @param key   Index of the new <code>SchemaCounter</code>. It is the key
     * of the counter minus the ID of current slot content.
     * @param count Reference to the <code>SchemaCounter</code> that should
     * be added to the list (actually a map) of counters.
     */
    public void addCount(int level, int index, int key, SchemaCounter count) {
        counts.getSchema(level, index).put(key, count);
    }

    /**
     * Getter variable for <code>counts</code> variable.
     * <p>
     * <code>counts</code> is a collection of all <code>SchemaCounter</code>s
     * this node contributes to. In order to quickly access any counter,
     * counters are indexed in a multihashmap. <strong>Index of a counter is calculated
     * as the sum of IDs of all nodes it refers to</strong>, but when accessing
     * a counter from the inside of the node, nodes id is not added to the sum.
     * So a single counters of nodes can always be accessed on zero index.
     *
     * @return  Returns a set of all schema counters this slot content is
     * participating in.
     */
    public MultiHashMap<Integer, SchemaCounter> getCounts() {
        return counts.getSchema();
    }

    public MultiHashMap<Integer, SchemaCounter> getCounts(int level, int index) {
        return counts.getSchema(level, index);
    }

    // Returns counts as iterable HashSet
    public HashSet<SchemaCounter> getCountsSet(MultiHashMap<Integer, SchemaCounter> counter) {
        HashSet<SchemaCounter> rv = new HashSet<SchemaCounter>();
        for (Entry<Integer, Collection<SchemaCounter>> e : counter.entrySet()) {
            rv.addAll(e.getValue());
        }
        return rv;
    }
}
